/*
 * @(#)JnlpResource.java 1.8 05/11/17 Copyright (c) 2006 Sun Microsystems, Inc.
 * All Rights Reserved. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that the following conditions
 * are met: -Redistribution of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer. -Redistribution
 * in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution. Neither the name of Sun
 * Microsystems, Inc. or the names of contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC.
 * ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You
 * acknowledge that this software is not designed, licensed or intended for use
 * in the design, construction, operation or maintenance of any nuclear
 * facility.
 */

package jnlp.sample.servlet;

import javax.servlet.ServletContext;
import java.net.URL;
import java.io.File;
import java.io.IOException;
import java.net.URLConnection;
import java.util.*;

/**
 * A JnlpResource encapsulate the information about a resource that is needed to
 * process a JNLP Download Request. The pattern matching arguments are: name,
 * version-id, os, arch, and locale. The outgoing arguments are: - path to
 * resource in (WAR File) - product version-id (Version-id to return or null.
 * Typically same as version-id above) - mime-type for content - lastModified
 * date of WAR file resource
 */
public class JnlpResource
{
    private static final String JNLP_MIME_TYPE = "application/x-java-jnlp-file";

    private static final String JAR_MIME_TYPE = "application/x-java-archive";

    private static final String JAR_MIME_TYPE_NEW = "application/java-archive";

    // Default extension for the JNLP file
    private static final String JNLP_EXTENSION = ".jnlp";

    private static final String JAR_EXTENSION = ".jar";

    private static String _jnlpExtension = JNLP_EXTENSION;

    private static String _jarExtension = JAR_EXTENSION;

    public static void setDefaultExtensions(String jnlpExtension, String jarExtension)
    {
	if (jnlpExtension != null && jnlpExtension.length() > 0)
	{
	    if (!jnlpExtension.startsWith("."))
		jnlpExtension = "." + jnlpExtension;
	    _jnlpExtension = jnlpExtension;
	}
	if (jarExtension != null && jarExtension.length() > 0)
	{
	    if (!jarExtension.startsWith("."))
		jarExtension = "." + jarExtension;
	    _jarExtension = jarExtension;
	}
    }

    /* Pattern matching arguments */
    private String _name; // Name of resource with path (this is the same as
			  // path for non-version based)

    private String _versionId; // Version-id for resource, or null if none

    private String[] _osList; // List of OSes for which resource should be
			      // returned

    private String[] _archList; // List of architectures for which the resource
				// should be returned

    private String[] _localeList; // List of locales for which the resource
				  // should be returned

    /* Information used for reply */
    private String _path; // Path to resource in WAR file (unique)

    private URL _resource; // URL to resource in WAR file (unique - same as
			   // above really)

    private long _lastModified; // Last modified in WAR file

    private String _mimeType; // Mime-type for resource

    private String _returnVersionId; // Version Id to return

    private String _encoding; // Accept encoding

    public JnlpResource(ServletContext context, String path)
    {
	this(context, null, null, null, null, null, path, null);
    }

    public JnlpResource(ServletContext context, String name, String versionId, String[] osList,
	    String[] archList, String[] localeList, String path, String returnVersionId)
    {
	this(context, name, versionId, osList, archList, localeList, path, returnVersionId, null);
    }

    public JnlpResource(ServletContext context, String name, String versionId, String[] osList,
	    String[] archList, String[] localeList, String path, String returnVersionId,
	    String encoding)
    {
	// Matching arguments
	_encoding = encoding;
	_name = name;
	_versionId = versionId;
	_osList = osList;
	_archList = archList;
	_localeList = localeList;

	_returnVersionId = returnVersionId;

	/* Check for existance and get last modified timestamp */
	try
	{
	    String orig_path = path.trim();
	    String search_path = orig_path;
	    _resource = context.getResource(orig_path);
	    _mimeType = getMimeType(context, orig_path);
	    if (_resource != null)
	    {

		boolean found = false;
		// pack200 compression
		if (encoding != null
			&& _mimeType != null
			&& (_mimeType.compareTo(JAR_MIME_TYPE) == 0 || _mimeType
				.compareTo(JAR_MIME_TYPE_NEW) == 0)
			&& encoding.toLowerCase().indexOf(DownloadResponse.PACK200_GZIP_ENCODING) > -1)
		{
		    search_path = orig_path + ".pack.gz";
		    _resource = context.getResource(search_path);
		    // Get last modified time
		    if (_resource != null)
		    {
			_lastModified = getLastModified(context, _resource, search_path);
			if (_lastModified != 0)
			{
			    _path = search_path;
			    found = true;
			}
			else
			{
			    _resource = null;
			}
		    }
		}

		// gzip compression
		if (found == false && encoding != null
			&& encoding.toLowerCase().indexOf(DownloadResponse.GZIP_ENCODING) > -1)
		{
		    search_path = orig_path + ".gz";
		    _resource = context.getResource(search_path);
		    // Get last modified time
		    if (_resource != null)
		    {
			_lastModified = getLastModified(context, _resource, search_path);
			if (_lastModified != 0)
			{
			    _path = search_path;
			    found = true;
			}
			else
			{
			    _resource = null;
			}
		    }
		}

		if (found == false)
		{
		    // no compression
		    search_path = orig_path;
		    _resource = context.getResource(search_path);
		    // Get last modified time
		    if (_resource != null)
		    {
			_lastModified = getLastModified(context, _resource, search_path);
			if (_lastModified != 0)
			{
			    _path = search_path;
			    found = true;
			}
			else
			{
			    _resource = null;
			}
		    }
		}
	    }
	}
	catch (IOException ioe)
	{
	    _resource = null;
	}
    }

    long getLastModified(ServletContext context, URL resource, String path)
    {
	long lastModified = 0;
	URLConnection conn;
	try
	{
	    // Get last modified time
	    conn = resource.openConnection();
	    lastModified = conn.getLastModified();
	}
	catch (Exception e)
	{
	    // do nothing
	}

	if (lastModified == 0)
	{
	    // Arguably a bug in the JRE will not set the lastModified for file
	    // URLs, and
	    // always return 0. This is a workaround for that problem.
	    String filepath = context.getRealPath(path);
	    if (filepath != null)
	    {
		File f = new File(filepath);
		if (f.exists())
		{
		    lastModified = f.lastModified();
		}
	    }
	}
	return lastModified;
    }

    /* Get resource specific attributes */
    public String getPath()
    {
	return _path;
    }

    public URL getResource()
    {
	return _resource;
    }

    public String getMimeType()
    {
	return _mimeType;
    }

    public long getLastModified()
    {
	return _lastModified;
    }

    public boolean exists()
    {
	return _resource != null;
    }

    public boolean isJnlpFile()
    {
	return _path.endsWith(_jnlpExtension);
    }

    public boolean isJarFile()
    {
	return _path.endsWith(_jarExtension);
    }

    /* Get JNLP version specific attributes */
    public String getName()
    {
	return _name;
    }

    public String getVersionId()
    {
	return _versionId;
    }

    public String[] getOSList()
    {
	return _osList;
    }

    public String[] getArchList()
    {
	return _archList;
    }

    public String[] getLocaleList()
    {
	return _localeList;
    }

    public String getReturnVersionId()
    {
	return _returnVersionId;
    }

    private String getMimeType(ServletContext context, String path)
    {
	String mimeType = context.getMimeType(path);
	if (mimeType != null)
	    return mimeType;
	if (path.endsWith(_jnlpExtension))
	    return JNLP_MIME_TYPE;
	if (path.endsWith(_jarExtension))
	    return JAR_MIME_TYPE;
	return "application/unknown";
    }

    /** Print info about an entry */
    public String toString()
    {
	return "JnlpResource[WAR Path: " + _path + showEntry(" versionId=", _versionId)
		+ showEntry(" name=", _name) + " lastModified=" + new Date(_lastModified)
		+ showEntry(" osList=", _osList) + showEntry(" archList=", _archList)
		+ showEntry(" localeList=", _localeList) + "]"
		+ showEntry(" returnVersionId=", _returnVersionId) + "]";

    }

    private String showEntry(String msg, String value)
    {
	if (value == null)
	    return "";
	return msg + value;
    }

    private String showEntry(String msg, String[] value)
    {
	if (value == null)
	    return "";
	return msg + java.util.Arrays.asList(value).toString();
    }
}
